; *******************************************************************
; * exp6_1.asm
; * LLP STUDIO Chen Zhepeng
; * V1.0
; * 2023-11-25
; * 通过旋转电位器改变输入电压值，读取ADC转换结果
; * 以十六进制形式显示在右侧4个数码管上。
; *******************************************************************

P4         DATA 0C0H
P6         DATA 0E8H
P0M1       DATA 093H
P0M0       DATA 094H
P4M1       DATA 0B3H
P4M0       DATA 0B4H
P6M1       DATA 0CBH
P6M0       DATA 0CCH
HC595_SI   BIT  P4.4    ; 74HC595串行输入端
HC595_SCK  BIT  P4.2    ; 74HC595移位时钟输入
HC595_RCK  BIT  P4.3    ; 74HC595锁存时钟输入
NIXIE8     DATA 30H     ; 显示缓冲30H ~ 37H
DISP_BLACK EQU  10H     ; 对应段码表0FFH（全黑）
DISP_DP    EQU  20H
AUXR       DATA 08EH
; ADC模拟通道
ADC0_P10   EQU  00H
ADC1_P11   EQU  01H
ADC2_P54   EQU  02H
ADC3_P13   EQU  03H
ADC4_P14   EQU  04H
ADC5_P15   EQU  05H
ADC6_P16   EQU  06H
ADC7_P17   EQU  07H
ADC8_P00   EQU  08H
ADC9_P01   EQU  09H
ADC10_P02  EQU  0AH
ADC11_P03  EQU  0BH
ADC12_P04  EQU  0CH
ADC13_P05  EQU  0DH
ADC14_P06  EQU  0EH
ADC_POWER  EQU  (1 SHL 7)
ADC_START  EQU  (1 SHL 6)
ADC_FLAG   EQU  (1 SHL 5)
ADC_CONTR  DATA 0BCH  ; ADC控制寄存器
ADC_RES    DATA 0BDH  ; ADC转换结果高8位寄存器
ADC_RESL   DATA 0BEH  ; ADC转换结果低8位寄存器
ADCCFG     DATA 0DEH  ; ADC配置寄存器
RESFMT     EQU  (1 SHL 5)
; 储存ADC结果寄存器
ADC13_H    DATA 3DH
ADC13_L    DATA 3EH
disp_index DATA 38H     ; 显示位索引
flag_1ms   BIT  20H.0  ; 1 ms标志
ms_h       DATA 39H
ms_l       DATA 3AH


ORG 0000H
LJMP Main
ORG 000BH
LJMP Tm0Isr
ORG 0100H
Main:
; P4.0设置成推挽输出，其余为准双向口
MOV P4M1, #00H
MOV P4M0, #01H
; P6设置为推挽输出
MOV P6M1, #00H
MOV P6M0, #0FFH
MOV SP, #80H
MOV PSW, #0
MOV disp_index, #0  ; 消隐
MOV R0, #NIXIE8  ; 灭灯
MOV R2, #8  ; 8个数码管，执行8次
SETB P4.0  ; 关闭8个LED
ClearLoop:
MOV @R0, #DISP_BLACK
INC R0
DJNZ R2, ClearLoop
CLR TR0
ORL AUXR, #80H  ; 1T模式，定时器0不分频
ANL TMOD, #0F0H  ; 定时器0方式0
MOV TH0, #0A2H
MOV TL0, #40H
SETB ET0
SETB TR0
SETB EA
LCALL AdcConfig
MainLoop:
JNB flag_1ms, MainLoop  ; 1 ms未到
CLR flag_1ms
; 检测300 ms是否达到，300 ms转换一次
INC ms_l
MOV A, ms_l
JNZ $ + 4  ; 2 Bytes
INC ms_h  ; 2 Bytes 逢十进一
; 如果ms_l每到0，也就不向ms_h进位，直接跳到这里开始判断有没有到300
CLR C
MOV A, ms_l  ; 2 Bytes
SUBB A, #LOW 300  ; ms - 300（012CH）的低8位（2CH）
MOV A, ms_h
SUBB A, #HIGH 300  ; ms - 300（012CH）的高8位（01H）
JC MainLoop  ; ms - 300要Cy：未到300 ms继续
; 300 ms到
MOV ms_l, #0
MOV ms_h, #0
MOV A, #ADC13_P05
LCALL GetAdcRes  ; 丢弃结果，让内部采样电容的电压等于输入值
MOV A, #ADC13_P05
; 读外部电压ADC，查询方式做一次ADC，
; 返回值(R6 R7)就是ADC结果，== 4096为错误
LCALL GetAdcRes
MOV ADC13_H, R6
MOV ADC13_L, R7
MOV A, R6
SWAP A
ANL A, #0FH
MOV NIXIE8 + 4, A
MOV A, R6
ANL A, #0FH
MOV NIXIE8 + 5, A
MOV A, R7
SWAP A
ANL A, #0FH
MOV NIXIE8 + 6, A
MOV A, R7
ANL A, #0FH
MOV NIXIE8 + 7, A
LJMP MainLoop

; * @brief       ADC配置
; * @param       无
; * @retval      无
AdcConfig:
MOV ADC_CONTR, #ADC_POWER  ; 打开ADC
ORL ADCCFG, #RESFMT  ; 转换结果右对齐，给ADC的工作时钟频率为SYSclk/2/1
; 配置P0.5（ADC13口）为高阻输入，其他为推挽输出
MOV P0M0, #0DFH
MOV P0M1, #20H
RET

; * @brief       获取ADC转换结果
; * @param       ACC: 通道号
; * @retval      R6: ADC转换结果的高4位，若超时为4096（1000H）的高8位（10H）
;                R7: ADC转换结果的低8位，若超时为4096（1000H）的低8位（00H）
GetAdcRes:
MOV R7, A  ; 通道号
MOV ADC_RES, #0
MOV ADC_RESL, #0
; 打开ADC电源，选择ADC模拟通道，开始ADC转换
MOV A, ADC_CONTR
ANL A, #ADC_POWER
ORL A, R7
ORL A, #ADC_START
MOV ADC_CONTR, A
NOP
NOP
NOP
NOP
MOV R3, #100
WaitLoop:
MOV A, ADC_CONTR
JNB ACC.5, CheckTimeOut  ; 检查ADC_FLAG，转换未完成检查是否超时
ANL ADC_CONTR, #NOT ADC_FLAG  ; 转换完成，EOC软件清零
MOV A, ADC_RES
ANL A, #0FH
MOV R6, A
MOV R7, ADC_RESL
SJMP QuitAdc
; 超时错误，返回4096
CheckTimeOut:
DJNZ R3, WaitLoop
MOV R6, #HIGH 4096
MOV R7, #LOW 4096
QuitAdc:
RET

PosTable:
DB 80H, 40H, 20H, 10H, 08H, 04H, 02H, 01H
SegTable:
DB 0XC0, 0XF9, 0XA4, 0XB0, 0X99, 0X92, 0X82, 0XF8
DB 0X80, 0X90, 0X88, 0X83, 0XC6, 0XA1, 0X86, 0X8E
DB 0FFH  ; 全黑

; * @brief       向74HC595D发送一个字节
; * @param       ACC: 要发送的字节数据（存放在累加器中）
; * @retval      无
Hc595SendByte:
PUSH 02H
MOV R2, #8
Hc595SendLoop:
RLC A
MOV HC595_SI, C
SETB HC595_SCK
CLR HC595_SCK
DJNZ R2, Hc595SendLoop
POP 02H
RET

; * @brief       数码管动态显示
; * @param       无
; * @retval      无
ScanDisp:
PUSH DPH
PUSH DPL
PUSH 00H
MOV A, #00H
LCALL Hc595SendByte  ; 输出点阵位码，关闭点阵
MOV DPTR, #PosTable
MOV A, disp_index
MOVC A, @A + DPTR
LCALL Hc595SendByte  ; 输出位码
CLR HC595_RCK
NOP
SETB HC595_RCK  ; 上升沿并行锁存到输出端
NOP
CLR HC595_RCK  ; 锁存输出数据
MOV DPTR, #SegTable
MOV A, disp_index
ADD A, #NIXIE8  ; 根据disp_index指示的第几个数码管，
MOV R0, A  ; 定位要显示的数码管
MOV A, @R0
MOVC A, @A + DPTR
MOV P6, A  ; 输出段码
INC disp_index
MOV A, disp_index
ANL A, #0F8H
JZ QuitScanDisp  ; disp_index == 0 ~ 7跳转
; if (disp_index >= 8)
; {
MOV disp_index, #0  ; 8位结束回0
; }
; else
; {
QuitScanDisp:
POP 00H
POP DPL
POP DPH
RET
; }

; * @brief       定时器0 1 ms中断函数
; * @param       无
; * @retval      无
Tm0Isr:
PUSH PSW
PUSH ACC
LCALL ScanDisp
SETB flag_1ms
POP ACC
POP PSW
RETI

END
